/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


// r_decal.cpp -- decal clipping and management


#include "r_local.h"


#define MAX_DECAL_FRAGMENTS		256
#define MAX_DECAL_VERTICES		768

typedef struct {
	int					triangle;
	int					indices[3];

	odVec3				normal;

	int					numVertices;
	int					firstVertex;
} decalFragment_t;

typedef struct {
	odVec3				origin;
	odVec3				direction;
	float				radius;

	float				startTime;
	float				endTime;

	material_t *		material;
	entity_t *			entity;

	odVec3				texVecs[2];

	odPlane				planes[6];

	int					numFragments;
	int					numIndices;
	int					numVertices;

	decalFragment_t		fragments[MAX_DECAL_FRAGMENTS];
	odVec3				vertices[MAX_DECAL_VERTICES];
} decalClip_t;


/*
 ==================
 R_FreeDecal
 ==================
*/
static void R_FreeDecal (scene_t *scene, decal_t *decal){

	// Free the base triangle/vertex pointers
	if (decal->baseModel){
		Mem_Free(decal->baseTriangles);
		Mem_Free(decal->baseVertices);
	}

	// Free the indices and vertices
	Mem_Free(decal->indices);
	Mem_Free(decal->vertices);

	// Unlink
	decal->prev->next = decal->next;
	decal->next->prev = decal->prev;

	decal->next = scene->freeDecal;
	scene->freeDecal = decal;
}

/*
 ==================
 R_AllocDecal
 ==================
*/
static void R_AllocDecal (scene_t *scene, decalClip_t *clip, surface_t *surface, model_t *baseModel, int baseSurface){

	decal_t			*decal;
	decalFragment_t	*fragment;
	int				*baseTriangle;
	int				*baseVertex;
	glIndex_t		*index;
	glVertex_t		*vertex;
	odVec3			delta;
	int				i, j;

	// If no free slots, free an old active one
	if (!scene->freeDecal)
		R_FreeDecal(scene, scene->activeDecal.prev);

	decal = scene->freeDecal;

	// Fill it in
	decal->startTime = clip->startTime;
	decal->endTime = clip->endTime;

	decal->material = clip->material;

	decal->parentEntity = clip->entity;
	decal->parentSurface = surface;

	decal->baseModel = baseModel;
	decal->baseSurface = baseSurface;

	// Allocate the base triangle/vertex pointers if needed
	if (baseModel){
		decal->baseTriangles = baseTriangle = (int *)Mem_Alloc(clip->numVertices * sizeof(int), TAG_RENDERER);
		decal->baseVertices = baseVertex = (int *)Mem_Alloc(clip->numVertices * sizeof(int), TAG_RENDERER);
	}

	// Allocate the indices and vertices
	decal->numIndices = clip->numIndices;
	decal->indices = index = (glIndex_t *)Mem_Alloc(decal->numIndices * sizeof(glIndex_t), TAG_RENDERER);

	decal->numVertices = clip->numVertices;
	decal->vertices = vertex = (glVertex_t *)Mem_Alloc(decal->numVertices * sizeof(glVertex_t), TAG_RENDERER);

	// Store the decal fragments
	for (i = 0, fragment = clip->fragments; i < clip->numFragments; i++, fragment++){
		// Set up the base triangle/vertex pointers if needed
		if (baseModel){
			*baseTriangle++ = fragment->triangle;
			*baseTriangle++ = fragment->triangle;
			*baseTriangle++ = fragment->triangle;

			*baseVertex++ = fragment->indices[0];
			*baseVertex++ = fragment->indices[1];
			*baseVertex++ = fragment->indices[2];
		}

		// Set up the indices
		for (j = 0; j < fragment->numVertices - 2; j++, index += 3){
			index[0] = fragment->firstVertex;
			index[1] = fragment->firstVertex + j + 1;
			index[2] = fragment->firstVertex + j + 2;
		}

		// Set up the vertices
		for (j = 0; j < fragment->numVertices; j++, vertex++){
			vertex->xyz = clip->vertices[fragment->firstVertex + j];

			vertex->normal = fragment->normal;

			delta = vertex->xyz - clip->origin;
			vertex->st[0] = clip->texVecs[0].Dot(delta) + 0.5f;
			vertex->st[1] = clip->texVecs[1].Dot(delta) + 0.5f;
		}
	}

	// Link
	scene->freeDecal = scene->freeDecal->next;

	decal->prev = &scene->activeDecal;
	decal->next = scene->activeDecal.next;

	scene->activeDecal.next->prev = decal;
	scene->activeDecal.next = decal;
}

/*
 ==================
 R_ClipDecal
 ==================
*/
static void R_ClipDecal (scene_t *scene, decalClip_t *clip, surface_t *surface, model_t *baseModel, int baseSurface, int triangle, const int *indices, const odVec3 &normal, int numVertices, odVec3 *vertices, int stage){

	decalFragment_t	*fragment;
	odVec3			clipped[MAX_DECAL_VERTICES];
	float			dists[MAX_DECAL_VERTICES];
	int				sides[MAX_DECAL_VERTICES];
	bool			front, back;
	int				i, numClipped;

	if (stage == 6){
		// Fully clipped, so add it
		if (numVertices < 3)
			return;

		// Check for overflow
		if (clip->numFragments == MAX_DECAL_FRAGMENTS || clip->numVertices + numVertices > MAX_DECAL_VERTICES){
			// Allocate a new decal
			R_AllocDecal(scene, clip, surface, baseModel, baseSurface);

			// Clear the counters
			clip->numFragments = 0;
			clip->numIndices = 0;
			clip->numVertices = 0;
		}

		// Add a temporary fragment
		fragment = &clip->fragments[clip->numFragments++];

		fragment->triangle = triangle;

		fragment->indices[0] = indices[0];
		fragment->indices[1] = indices[1];
		fragment->indices[2] = indices[2];

		fragment->normal = normal;

		fragment->numVertices = numVertices;
		fragment->firstVertex = clip->numVertices;

		for (i = 0; i < numVertices; i++)
			clip->vertices[clip->numVertices + i] = vertices[i];

		clip->numIndices += (numVertices - 2) * 3;
		clip->numVertices += numVertices;

		return;
	}

	if (numVertices > MAX_DECAL_VERTICES - 2)
		Com_Error(false, "R_ClipDecal: MAX_DECAL_VERTICES hit");

	// Determine sides for each vertex
	front = false;
	back = false;

	for (i = 0; i < numVertices; i++){
		dists[i] = clip->planes[stage].Distance(vertices[i]);

		if (dists[i] > ON_EPSILON){
			sides[i] = PLANESIDE_FRONT;
			front = true;
			continue;
		}

		if (dists[i] < -ON_EPSILON){
			sides[i] = PLANESIDE_BACK;
			back = true;
			continue;
		}

		sides[i] = PLANESIDE_ON;
	}

	if (!front)
		return;		// Not clipped

	if (!back || baseModel){
		// Continue
		R_ClipDecal(scene, clip, surface, baseModel, baseSurface, triangle, indices, normal, numVertices, vertices, stage+1);
		return;
	}

	// Clip it
	vertices[i] = vertices[0];

	dists[i] = dists[0];
	sides[i] = sides[0];

	numClipped = 0;

	for (i = 0; i < numVertices; i++){
		if (sides[i] == PLANESIDE_ON){
			clipped[numClipped++] = vertices[i];
			continue;
		}

		if (sides[i] == PLANESIDE_FRONT)
			clipped[numClipped++] = vertices[i];

		if (sides[i+1] == PLANESIDE_ON || sides[i+1] == sides[i])
			continue;

		if (dists[i] == dists[i+1])
			clipped[numClipped++] = vertices[i];
		else
			clipped[numClipped++].Lerp(vertices[i], vertices[i+1], dists[i] / (dists[i] - dists[i+1]));
	}

	// Continue
	R_ClipDecal(scene, clip, surface, baseModel, baseSurface, triangle, indices, normal, numClipped, clipped, stage+1);
}


/*
 ==============================================================================

 WORLD MODEL

 ==============================================================================
*/


/*
 ==================
 R_ClipDecalToSurface
 ==================
*/
static void R_ClipDecalToSurface (scene_t *scene, decalClip_t *clip, surface_t *surface){

	glIndex_t	*index;
	odPlane		*facePlane;
	odVec3		vertices[4];
	int			numTriangles;
	int			i;

	// Clear the counters
	clip->numFragments = 0;
	clip->numIndices = 0;
	clip->numVertices = 0;

	// Clip to the surface
	numTriangles = surface->numIndices / 3;

	if (surface->plane){
		if (clip->direction.Dot(surface->plane->normal) < 0.25f)
			return;			// Greater than 75 degrees

		// Clip to each triangle
		for (i = 0, index = surface->indices; i < numTriangles; i++, index += 3){
			vertices[0] = surface->vertices[index[0]].xyz;
			vertices[1] = surface->vertices[index[1]].xyz;
			vertices[2] = surface->vertices[index[2]].xyz;

			R_ClipDecal(scene, clip, surface, NULL, -1, i, (const int *)index, surface->plane->normal, 3, vertices, 0);
		}
	}
	else {
		// Clip to each triangle
		for (i = 0, index = surface->indices, facePlane = surface->facePlanes; i < numTriangles; i++, index += 3, facePlane++){
			if (clip->direction.Dot(facePlane->normal) < 0.25f)
				continue;		// Greater than 75 degrees

			vertices[0] = surface->vertices[index[0]].xyz;
			vertices[1] = surface->vertices[index[1]].xyz;
			vertices[2] = surface->vertices[index[2]].xyz;

			R_ClipDecal(scene, clip, surface, NULL, -1, i, (const int *)index, facePlane->normal, 3, vertices, 0);
		}
	}

	if (!clip->numFragments)
		return;			//Not clipped

	// Allocate a new decal
	R_AllocDecal(scene, clip, surface, NULL, -1);
}

/*
 ==================
 R_RecursiveDecalNode
 ==================
*/
static void R_RecursiveDecalNode (scene_t *scene, decalClip_t *clip, node_t *node){

	surface_t	*surface;
	int			i, side;

	// If a node, recurse down the children
	if (!node->isLeaf){
		// Find which side of the node we are on
		side = node->plane->SphereOnSide(clip->origin, clip->radius);

		// Go down the appropriate sides
		if (side == PLANESIDE_FRONT){
			R_RecursiveDecalNode(scene, clip, node->children[0]);
			return;
		}
		if (side == PLANESIDE_BACK){
			R_RecursiveDecalNode(scene, clip, node->children[1]);
			return;
		}

		R_RecursiveDecalNode(scene, clip, node->children[0]);
		R_RecursiveDecalNode(scene, clip, node->children[1]);

		return;
	}

	// Clip to each surface
	for (i = 0; i < node->numLeafSurfaces; i++){
		surface = node->leafSurfaces[i];

		if (surface->fragmentCount == tr.fragmentCount)
			continue;		// Already checked this surface in another leaf
		surface->fragmentCount = tr.fragmentCount;

		if (surface->type == ST_FOLIAGE)
			continue;		// Not an actual surface

		if (surface->material->flags & MF_NOOVERLAYS)
			continue;		// Don't bother clipping

		if (!surface->bounds.IntersectsSphere(clip->origin, clip->radius))
			continue;		// No intersection

		// Clip to the surface
		R_ClipDecalToSurface(scene, clip, surface);
	}
}


/*
 ==============================================================================

 INLINE MODELS

 ==============================================================================
*/


/*
 ==================
 R_ClipDecalToInlineModel
 ==================
*/
static void R_ClipDecalToInlineModel (scene_t *scene, decalClip_t *clip){

	inlineModel_t	*model = (inlineModel_t *)clip->entity->e.model->data;
	surface_t		*surface;
	glIndex_t		*index;
	odPlane			*facePlane;
	odVec3			vertices[4];
	int				numTriangles;
	int				i, j;

	// Perform a callback if needed
	if (clip->entity->e.callback){
		clip->entity->e.callback(&clip->entity->e);

		clip->entity->e.callback = NULL;
	}

	// Clip to each surface
	for (i = 0, surface = model->surfaces; i < model->numSurfaces; i++, surface++){
		if (surface->type == ST_FOLIAGE)
			continue;		// Not an actual surface

		if (surface->material->flags & MF_NOOVERLAYS)
			continue;		// Don't bother clipping

		if (!surface->bounds.IntersectsSphere(clip->origin, clip->radius))
			continue;		// No intersection

		// Clear the counters
		clip->numFragments = 0;
		clip->numIndices = 0;
		clip->numVertices = 0;

		// Clip to the surface
		numTriangles = surface->numIndices / 3;

		if (surface->plane){
			if (clip->direction.Dot(surface->plane->normal) < 0.25f)
				continue;		// Greater than 75 degrees

			// Clip to each triangle
			for (j = 0, index = surface->indices; j < numTriangles; j++, index += 3){
				vertices[0] = surface->vertices[index[0]].xyz;
				vertices[1] = surface->vertices[index[1]].xyz;
				vertices[2] = surface->vertices[index[2]].xyz;

				R_ClipDecal(scene, clip, NULL, NULL, -1, j, (const int *)index, surface->plane->normal, 3, vertices, 0);
			}
		}
		else {
			// Clip to each triangle
			for (j = 0, index = surface->indices, facePlane = surface->facePlanes; j < numTriangles; j++, index += 3, facePlane++){
				if (clip->direction.Dot(facePlane->normal) < 0.25f)
					continue;		// Greater than 75 degrees

				vertices[0] = surface->vertices[index[0]].xyz;
				vertices[1] = surface->vertices[index[1]].xyz;
				vertices[2] = surface->vertices[index[2]].xyz;

				R_ClipDecal(scene, clip, NULL, NULL, -1, j, (const int *)index, facePlane->normal, 3, vertices, 0);
			}
		}

		if (!clip->numFragments)
			continue;		// Not clipped

		// Allocate a new decal
		R_AllocDecal(scene, clip, NULL, NULL, -1);
	}
}


/*
 ==============================================================================

 MDL MODELS

 ==============================================================================
*/


/*
 ==================
 R_ClipDecalToMDLModel
 ==================
*/
static void R_ClipDecalToMDLModel (scene_t *scene, decalClip_t *clip){

	mdlModel_t		*model = (mdlModel_t *)clip->entity->e.model->data;
	mdlSurface_t	*surface;
	glIndex_t		*index;
	odPlane			*facePlane;
	odVec3			vertices[4];
	int				numTriangles;
	int				i, j;

	// Perform a callback if needed
	if (clip->entity->e.callback){
		clip->entity->e.callback(&clip->entity->e);

		clip->entity->e.callback = NULL;
	}

	// Clip to each surface
	for (i = 0, surface = model->surfaces; i < model->numSurfaces; i++, surface++){
		if (surface->material->flags & MF_NOOVERLAYS)
			continue;		// Don't bother clipping

		if (!surface->bounds.IntersectsSphere(clip->origin, clip->radius))
			continue;		// No intersection

		// Clear the counters
		clip->numFragments = 0;
		clip->numIndices = 0;
		clip->numVertices = 0;

		// Clip to each triangle
		numTriangles = surface->numIndices / 3;

		for (j = 0, index = surface->indices, facePlane = surface->facePlanes; j < numTriangles; j++, index += 3, facePlane++){
			if (clip->direction.Dot(facePlane->normal) < 0.25f)
				continue;		// Greater than 75 degrees

			vertices[0] = surface->vertices[index[0]].xyz;
			vertices[1] = surface->vertices[index[1]].xyz;
			vertices[2] = surface->vertices[index[2]].xyz;

			R_ClipDecal(scene, clip, NULL, NULL, -1, j, (const int *)index, facePlane->normal, 3, vertices, 0);
		}

		if (!clip->numFragments)
			continue;		//Not clipped

		// Allocate a new decal
		R_AllocDecal(scene, clip, NULL, NULL, -1);
	}
}


/*
 ==============================================================================

 MD5 MODELS

 ==============================================================================
*/

/*
static void R_ClipDecalToMD5Model (scene_t *scene, decalClip_t *clip){

	mdlModel_t		*model;
	mdlSurface_t	*surface;
	glIndex_t		*index;
	odPlane			*facePlane;
	odVec3			vertices[4];
	int				numTriangles;
	int				i, j;

	// Perform a callback if needed
	if (clip->entity->e.callback){
		clip->entity->e.callback(&clip->entity->e);

		clip->entity->e.callback = NULL;
	}

	// Instantiate
	model = R_InstantiateDynamicModel(clip->entity, false, false);

	// Clip to each surface
	for (i = 0, surface = model->surfaces; i < model->numSurfaces; i++, surface++){
		if (surface->material->flags & MF_NOOVERLAYS)
			continue;		// Don't bother clipping

		if (!surface->bounds.IntersectsSphere(clip->origin, clip->radius))
			continue;		// No intersection

		// Clear the counters
		clip->numFragments = 0;
		clip->numIndices = 0;
		clip->numVertices = 0;

		// Clip to each triangle
		numTriangles = surface->numIndices / 3;

		for (j = 0, index = surface->indices, facePlane = surface->facePlanes; j < numTriangles; j++, index += 3, facePlane++){
			if (clip->direction.Dot(facePlane->normal) < 0.25f)
				continue;		// Greater than 75 degrees

			vertices[0] = surface->vertices[index[0]].xyz;
			vertices[1] = surface->vertices[index[1]].xyz;
			vertices[2] = surface->vertices[index[2]].xyz;

			R_ClipDecal(scene, clip, NULL, clip->entity->e.model, i, j, (const int *)index, facePlane->normal, 3, vertices, 0);
		}

		if (!clip->numFragments)
			continue;		//Not clipped

		// Allocate a new decal
		R_AllocDecal(scene, clip, NULL, clip->entity->e.model, i);
	}
}
*/

// ============================================================================


/*
 ==================
 R_AddDecals
 ==================
*/
void R_AddDecals (void){

	decal_t	*decal, *next;

	if (r_skipDecals->integerValue)
		return;

	for (decal = tr.scene->activeDecal.next; decal != &tr.scene->activeDecal; decal = next){
		// Grab next now, so if the decal is freed we still have it
		next = decal->next;

		// Check if completely faded out
		if (tr.renderView.time >= decal->endTime){
			R_FreeDecal(tr.scene, decal);
			continue;
		}

		// Check if the parent entity is visible
		if (decal->parentEntity != tr.scene->worldEntity){
			if (decal->parentEntity->viewCount != tr.viewCount)
				continue;
		}

		// Check if the parent surface is visible
		if (decal->parentSurface != NULL){
			if (decal->parentSurface->viewCount != tr.viewCount)
				continue;
		}

		tr.pc.decals++;

		// If it has a base model, set up the instance
		if (decal->baseModel){
			if (decal->baseModel != decal->parentEntity->e.model)
				Com_Error(false, "R_AddDecals: model has changed since generating the decal");

			// Perform a callback if needed
			if (decal->parentEntity->e.callback){
				decal->parentEntity->e.callback(&decal->parentEntity->e);

				decal->parentEntity->e.callback = NULL;
			}

			// Instantiate
			//R_InstantiateDynamicModel(decal->parentEntity, false, false);
		}

		// Add the draw mesh
		R_AddDrawMesh(MESH_DECAL, decal, decal->material, decal->parentEntity);
	}
}

/*
 ==================
 R_ProjectDecalOntoWorld
 ==================
*/
void R_ProjectDecalOntoWorld (scene_t *scene, const odVec3 &origin, const odVec3 &direction, float angle, float radius, float time, material_t *material){

	decalClip_t	clip;
	odPlane		*plane;
	odVec4		color;
	odMat3		axis;
	odVec3		right, up;
	float		s, c;
	int			i;

	if (scene->inUse)
		Com_Error(false, "R_ProjectDecalOntoWorld: scene is currently in use");

	if (!tr.world)
		Com_Error(false, "R_ProjectDecalOntoWorld: NULL world");

	if (!scene->decals)
		return;		// No decals in this scene

	if (!material->numStages || (material->decalInfo.stayTime + material->decalInfo.fadeTime <= 0.0f))
		return;		// Don't bother drawing

	// Bump fragment count
	tr.fragmentCount++;

	// Fill in the clip structure
	clip.origin = origin;
	clip.direction = direction;
	clip.radius = radius;

	clip.startTime = time;
	clip.endTime = time + (material->decalInfo.stayTime + material->decalInfo.fadeTime);

	clip.material = material;
	clip.entity = scene->worldEntity;

	// Compute the orientation vectors
	M_SinCos(DEG2RAD(angle), &s, &c);

	direction.NormalVectors(right, up);

	axis[0] = direction;
	axis[1] = (right * c) - (up * s);
	axis[2] = (up * c) + (right * s);

	// Compute the texture vectors
	clip.texVecs[0] = axis[1] * (0.5f / radius);
	clip.texVecs[1] = axis[2] * (0.5f / radius);

	// Set up the clipping planes
	for (i = 0, plane = clip.planes; i < 6; i++, plane++){
		if (i & 1){
			plane->normal = -axis[i>>1];
			plane->dist = -origin.Dot(axis[i>>1]) - radius;
		}
		else {
			plane->normal = axis[i>>1];
			plane->dist = origin.Dot(axis[i>>1]) - radius;
		}

		plane->SetType();
		plane->SetSignBits();
	}

	// Clip the decal
	R_RecursiveDecalNode(scene, &clip, tr.world->nodes);
}

/*
 ==================
 R_ProjectDecal
 ==================
*/
void R_ProjectDecal (scene_t *scene, int entityHandle, const odVec3 &origin, const odVec3 &direction, float angle, float radius, float time, material_t *material){

	decalClip_t	clip;
	entity_t	*entity;
	odPlane		*plane;
	odVec3		localOrigin, localDirection;
	odVec4		color;
	odMat3		axis;
	odVec3		right, up;
	float		s, c;
	int			i;

	if (scene->inUse)
		Com_Error(false, "R_ProjectDecal: scene is currently in use");

	if (entityHandle < 0 || entityHandle >= scene->maxEntities)
		Com_Error(false, "R_ProjectDecal: entityHandle out of range");

	entity = &scene->entities[entityHandle];

	if (!entity->active){
		Com_Printf(S_COLOR_YELLOW "R_ProjectDecal: entityHandle %i is not active\n", entityHandle);
		return;
	}

	if (entity->e.type != RE_MODEL || (!entity->e.model || entity->e.model->type == MODEL_BAD))
		return;		// Invalid entity or model type

	if (!scene->decals)
		return;		// No decals in this scene

	if (!material->numStages || (material->decalInfo.stayTime + material->decalInfo.fadeTime <= 0.0f))
		return;		// Don't bother drawing

	// Transform into local space
	entity->e.axis.ProjectVector(origin - entity->e.origin, localOrigin);
	entity->e.axis.ProjectVector(direction, localDirection);

	// Fill in the clip structure
	clip.origin = localOrigin;
	clip.direction = localDirection;
	clip.radius = radius;

	clip.startTime = time;
	clip.endTime = time + (material->decalInfo.stayTime + material->decalInfo.fadeTime);

	clip.material = material;
	clip.entity = entity;

	// Compute the orientation vectors
	M_SinCos(DEG2RAD(angle), &s, &c);

	localDirection.NormalVectors(right, up);

	axis[0] = localDirection;
	axis[1] = (right * c) - (up * s);
	axis[2] = (up * c) + (right * s);

	// Compute the texture vectors
	clip.texVecs[0] = axis[1] * (0.5f / radius);
	clip.texVecs[1] = axis[2] * (0.5f / radius);

	// Set up the clipping planes
	for (i = 0, plane = clip.planes; i < 6; i++, plane++){
		if (i & 1){
			plane->normal = -axis[i>>1];
			plane->dist = -origin.Dot(axis[i>>1]) - radius;
		}
		else {
			plane->normal = axis[i>>1];
			plane->dist = origin.Dot(axis[i>>1]) - radius;
		}

		plane->SetType();
		plane->SetSignBits();
	}

	// Clip the decal
	switch (entity->e.model->type){
	case MODEL_INLINE:
		R_ClipDecalToInlineModel(scene, &clip);
		break;
	case MODEL_MDL:
		R_ClipDecalToMDLModel(scene, &clip);
		break;
	//case MODEL_MD5:
	//	R_ClipDecalToMD5Model(scene, &clip);
	//	break;
	default:
		Com_Error(false, "R_ProjectDecal: bad model type (%i)", entity->e.model->type);
	}
}

/*
 ==================
 R_RemoveDecals
 ==================
*/
void R_RemoveDecals (scene_t *scene, int entityHandle){

	entity_t	*entity;
	decal_t		*decal, *next;

	if (scene->inUse)
		Com_Error(false, "R_RemoveDecals: scene is currently in use");

	if (entityHandle < 0 || entityHandle >= scene->maxEntities)
		Com_Error(false, "R_RemoveDecals: entityHandle out of range");

	entity = &scene->entities[entityHandle];

	if (!entity->active){
		Com_Printf(S_COLOR_YELLOW "R_RemoveDecals: entityHandle %i is not active\n", entityHandle);
		return;
	}

	if (!scene->decals)
		return;		// No decals in this scene

	// Remove any decals from this entity
	for (decal = scene->activeDecal.next; decal != &scene->activeDecal; decal = next){
		// Grab next now, so if the decal is freed we still have it
		next = decal->next;

		if (decal->parentEntity != entity)
			continue;

		R_FreeDecal(scene, decal);
	}
}
